package org.amphiprion.diceroller.util;

import java.util.HashMap;

import org.amphiprion.diceroller.TagObject;
import org.amphiprion.diceroller.entity.DiceDefinition;
import org.amphiprion.diceroller.entity.DiceFaceDefinition;
import org.amphiprion.diceroller.entity.GameDice;
import org.amphiprion.gameengine.Mesh;
import org.amphiprion.gameengine.util.MathUtil;
import org.amphiprion.gameengine.util.Vector3;

import android.opengl.Matrix;

public class DiceUtil {
	public static String getResultFaceNumber(HashMap<String, DiceDefinition> diceDefinitions, Mesh die) {
		GameDice gameDice = ((TagObject) die.getTagObject()).gameDice;
		DiceDefinition def = diceDefinitions.get(gameDice.getType());

		float[] vector = new float[] { def.getNormalForResult()[0], def.getNormalForResult()[1], def.getNormalForResult()[2], 1 };
		vector = rotate(vector, die.rotXDegree, 1, 0, 0);
		vector = rotate(vector, -die.rotZDegree, 0, 0, 1);
		vector = rotate(vector, die.rotYDegree, 0, 1, 0);
		// vector = rotate(vector, -die.rotationDegree, (float) die.rotationVector.x(), (float) die.rotationVector.y(), (float) die.rotationVector.z());
		// Log.d(ApplicationConstants.PACKAGE, "vector up   x=" + vector[0] + "  y=" + vector[1] + "  z=" + vector[2] + "    rotXDegree=" + die.rotXDegree + "  rotYDegree=" + die.rotYDegree + "  rotZDegree=" + die.rotZDegree);
		String selectedFaceNumber = null;
		float minDist = 10000000000f;
		float dx;
		float dy;
		float dz;
		float dist;
		for (DiceFaceDefinition faceDef : def.getFaceDefinitions()) {
			dx = faceDef.getNormal()[0] - vector[0];
			dy = faceDef.getNormal()[1] - vector[1];
			dz = faceDef.getNormal()[2] - vector[2];
			dist = dx * dx + dy * dy + dz * dz;
			if (dist <= minDist) {
				minDist = dist;
				selectedFaceNumber = faceDef.getNumber();
			}

		}
		return selectedFaceNumber;
	}

	public static float[] rotate(float[] inVec, float angle, float axisX, float axisY, float axisZ) {
		float[] matrix = new float[16];
		Matrix.setIdentityM(matrix, 0);
		Matrix.rotateM(matrix, 0, angle, axisX, axisY, axisZ);
		float[] outVec = new float[4];
		Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0);
		return outVec;
	}

	/**
	 * [0] = angleXDegree [1] = angleYDegree [2] = angleZDegree
	 * 
	 * @param diceDefinitions
	 * @param die
	 * @param faceIndex
	 * @return
	 */
	public static float[] getRotationToFace(HashMap<String, DiceDefinition> diceDefinitions, GameDice gameDice, int faceIndex) {
		float[] result = new float[3];

		DiceDefinition diceDefinition = diceDefinitions.get(gameDice.getType());

		DiceFaceDefinition dfd = diceDefinition.getFaceDefinitions().get(faceIndex);
		// Log.d(ApplicationConstants.PACKAGE, "******* random " + gameDice.getId() + "  facenumber=" + dfd.getNumber());

		Vector3 normal1 = new Vector3(dfd.getNormal()).normalize();

		Vector3 normal2 = new Vector3(diceDefinition.getNormalForResult()).normalize();
		Vector3 axis = normal1.cross(normal2);
		// Log.d(ApplicationConstants.PACKAGE, "normal1=" + normal1 + "  normal2=" + normal2 + "  axis=" + axis);
		double angle = 0;
		if (Math.abs(axis.norm()) < 0.0001) {
			if (Math.abs(normal1.z()) > 0.1) {
				axis = new Vector3(1, 1, -(normal1.x() + normal1.y()) / normal1.z());
			} else if (Math.abs(normal1.x()) > 0.1) {
				axis = new Vector3(-(normal1.z() + normal1.y()) / normal1.x(), 1, 1);
			} else if (Math.abs(normal1.y()) > 0.1) {
				axis = new Vector3(1, -(normal1.x() + normal1.z()) / normal1.y(), 1);
			}
			// Log.d(ApplicationConstants.PACKAGE, "   after tweek   axis=" + axis);
			double dx;
			double dy;
			double dz;
			double dist;
			dx = normal1.x() - normal2.x();
			dy = normal1.y() - normal2.y();
			dz = normal1.z() - normal2.z();
			dist = dx * dx + dy * dy + dz * dz;
			if (dist < 0.1) {
				angle = 0;
			} else {
				angle = Math.PI;
			}
		} else {
			angle = Math.acos(normal1.dot(normal2));
		}
		axis = axis.normalize();

		// Log.d(ApplicationConstants.PACKAGE, "     angle=" + angle);

		double[] euler = MathUtil.toEuler(axis.x(), axis.y(), axis.z(), angle);
		result[0] = -(float) euler[0];
		result[1] = (float) euler[2];
		result[2] = (float) euler[1];

		return result;
	}
}
